﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Windows.Shapes;
using System.Windows.Media;

namespace Gronia.WPF.Controls
{
    [ContentProperty("Children")]
    public class ConnectContainer : ScrollViewer
    {
        static ConnectContainer()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ConnectContainer), new FrameworkPropertyMetadata(typeof(ConnectContainer)));
        }

        #region Dependency Properties

        public static readonly DependencyProperty ChildrenProperty = DependencyProperty.Register("Children", typeof(ObservableCollection<ConnectionChild>), typeof(ConnectContainer), new PropertyMetadata(new ObservableCollection<ConnectionChild>(), new PropertyChangedCallback(ConnectContainer.OnChildrenChanged)));
        public static readonly DependencyProperty ConnectorDashArrayProperty = DependencyProperty.Register("ConnectorDashArray", typeof(DoubleCollection), typeof(ConnectContainer), new PropertyMetadata(new DoubleCollection(), new PropertyChangedCallback(ConnectContainer.OnPropertyChanged)));
        public static readonly DependencyProperty ConnectorDashCapProperty = DependencyProperty.Register("ConnectorDashCap", typeof(PenLineCap), typeof(ConnectContainer), new PropertyMetadata(PenLineCap.Flat, new PropertyChangedCallback(ConnectContainer.OnPropertyChanged)));
        public static readonly DependencyProperty ConnectorDashOffsetProperty = DependencyProperty.Register("ConnectorDashOffset", typeof(double), typeof(ConnectContainer), new PropertyMetadata(0.0, new PropertyChangedCallback(ConnectContainer.OnPropertyChanged)));
        public static readonly DependencyProperty ConnectorEndLineCapProperty = DependencyProperty.Register("ConnectorEndLineCap", typeof(PenLineCap), typeof(ConnectContainer), new PropertyMetadata(PenLineCap.Flat, new PropertyChangedCallback(ConnectContainer.OnPropertyChanged)));
        public static readonly DependencyProperty ConnectorStartLineCapProperty = DependencyProperty.Register("ConnectorStartLineCap", typeof(PenLineCap), typeof(ConnectContainer), new PropertyMetadata(PenLineCap.Flat, new PropertyChangedCallback(ConnectContainer.OnPropertyChanged)));
        public static readonly DependencyProperty ConnectorStrokeProperty = DependencyProperty.Register("ConnectorStroke", typeof(Brush), typeof(ConnectContainer), new PropertyMetadata(new SolidColorBrush(Colors.Black), new PropertyChangedCallback(ConnectContainer.OnPropertyChanged)));
        public static readonly DependencyProperty ConnectorThicknessProperty = DependencyProperty.Register("ConnectorThickness", typeof(double), typeof(ConnectContainer), new PropertyMetadata(1.0, new PropertyChangedCallback(ConnectContainer.OnPropertyChanged)));

        #endregion

        #region Properties

        public ObservableCollection<ConnectionChild> Children
        {
            get
            {
                return (ObservableCollection<ConnectionChild>)GetValue(ChildrenProperty);
            }
            set
            {
                SetValue(ChildrenProperty, value);
            }
        }
        public DoubleCollection ConnectorDashArray
        {
            get
            {
                return (DoubleCollection)GetValue(ConnectorDashArrayProperty);
            }
            set
            {
                base.SetValue(ConnectorDashArrayProperty, value);
            }
        }
        public PenLineCap ConnectorDashCap
        {
            get
            {
                return (PenLineCap)GetValue(ConnectorDashCapProperty);
            }
            set
            {
                base.SetValue(ConnectorDashCapProperty, value);
            }
        }
        public double ConnectorDashOffset
        {
            get
            {
                return (double)GetValue(ConnectorDashOffsetProperty);
            }
            set
            {
                base.SetValue(ConnectorDashOffsetProperty, value);
            }
        }
        public PenLineCap ConnectorEndLineCap
        {
            get
            {
                return (PenLineCap)GetValue(ConnectorEndLineCapProperty);
            }
            set
            {
                base.SetValue(ConnectorEndLineCapProperty, value);
            }
        }
        public PenLineCap ConnectorStartLineCap
        {
            get
            {
                return (PenLineCap)GetValue(ConnectorStartLineCapProperty);
            }
            set
            {
                base.SetValue(ConnectorStartLineCapProperty, value);
            }
        }
        public Brush ConnectorStroke
        {
            get
            {
                return (Brush)GetValue(ConnectorStrokeProperty);
            }
            set
            {
                base.SetValue(ConnectorStrokeProperty, value);
            }
        }
        public double ConnectorThickness
        {
            get
            {
                return (double)GetValue(ConnectorThicknessProperty);
            }
            set
            {
                base.SetValue(ConnectorThicknessProperty, value);
            }
        }

        #endregion

        private void Connections_Changed(object sender, NotifyCollectionChangedEventArgs e)
        {
            Redraw();
        }

        private void Children_Changed(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                foreach (UIElement ui in e.NewItems)
                {
                    canvas.Children.Add(ui);
                }
            }

            if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (UIElement ui in e.OldItems)
                {
                    canvas.Children.Remove(ui);
                }
            }

            Redraw();
        }

        private static void OnChildrenChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ConnectContainer me = sender as ConnectContainer;

            if (e.OldValue != null)
            {
                ObservableCollection<ConnectionChild> child = e.OldValue as ObservableCollection<ConnectionChild>;
                //  RemoveAll(child);
            }

            if (e.NewValue != null)
            {
                //AddAll(me.Children);
            }
            me.Redraw();
        }

        private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ConnectContainer container = sender as ConnectContainer;
            container.Redraw();
        }

        public ObservableCollection<Connection> Connections = new ObservableCollection<Connection>();

        private Canvas canvas;

        public ConnectContainer()
        {
            Connections.CollectionChanged += Connections_Changed;
            Children.CollectionChanged += Children_Changed;
            canvas = new Canvas();
            Content = canvas;
        }

        protected override void OnContentChanged(object oldContent, object newContent)
        {
            this.Content = canvas;
            base.OnContentChanged(oldContent, newContent);
        }

        internal void Redraw()
        {
            List<UIElement> remove = new List<UIElement>();
            foreach (UIElement child in canvas.Children)
            {
                if (child is Path)
                {
                    remove.Add(child);
                }
            }

            foreach (UIElement r in remove)
            {
                canvas.Children.Remove(r);
            }

            foreach (Connection c in Connections)
            {
                Path p = CreatePath(c);
                canvas.Children.Add(p);
            }
        }

        internal Path CreatePath(Connection connection)
        {
            Path path = new Path();

            path.HorizontalAlignment = HorizontalAlignment.Left;
            path.VerticalAlignment = VerticalAlignment.Top;
            path.Stroke = ConnectorStroke;
            path.StrokeDashArray = ConnectorDashArray;
            path.StrokeDashCap = ConnectorDashCap;
            path.StrokeDashOffset = ConnectorDashOffset;
            path.StrokeEndLineCap = ConnectorEndLineCap;
            path.StrokeStartLineCap = ConnectorStartLineCap;
            path.StrokeThickness = ConnectorThickness;
            connection.Path = path;
            PathGeometry geometry = new PathGeometry();
            PathFigure figure = new PathFigure();
            figure.StartPoint = connection.From.GetPosition();
            BezierSegment segment = CreateSegment(connection);
            figure.Segments.Add(segment);
            geometry.Figures.Add(figure);
            path.Data = geometry;
            Canvas.SetZIndex(path, -100);
            return path;
        }

        internal BezierSegment CreateSegment(Connection connection)
        {
            BezierSegment segment = new BezierSegment();
            segment.Point1 = new Point(connection.To.GetPosition().X, connection.From.GetPosition().Y);
            segment.Point2 = new Point(connection.From.GetPosition().X, connection.To.GetPosition().Y);
            segment.Point3 = connection.To.GetPosition();
            return segment;
        }

        internal void Redraw(Connection connection)
        {
            Path p = connection.Path;
            PathGeometry geometry = new PathGeometry();
            PathFigure figure = new PathFigure();
            figure.StartPoint = connection.From.GetPosition();
            BezierSegment segment = CreateSegment(connection);
            figure.Segments.Add(segment);
            geometry.Figures.Add(figure);
            p.Data = geometry;
        }

        internal void Redraw(ConnectionChild child)
        {
            foreach (ConnectPoint point in child.OutputPoints)
            {
                foreach (Connection c in Connections.Where(t => t.From == point || t.To == point))
                {
                    Redraw(c);
                }
            }

            foreach (ConnectPoint point in child.InputPoints)
            {
                foreach (Connection c in Connections.Where(t => t.To == point || t.From == point))
                {
                    Redraw(c);
                }
            }
        }

        internal void Connect(ConnectPoint From, ConnectPoint To)
        {
            Connection connection = Connections.FirstOrDefault(t => (t.From == From && t.To == To) || (t.From == To && t.To == From));
            if (connection == null)
            {
                connection = new Connection();
                connection.From = From;
                connection.To = To;
            }
            Connections.Add(connection);
        }

        internal void Disconnect(ConnectPoint Point)
        {
           var remove1 =  Connections.Where(t => t.From == Point || t.To == Point);
           List<Connection> remove = new List<Connection>();
           remove.AddRange(remove1);
           foreach (var a in remove)
           {
               Connections.Remove(a);
           }
        }
    }
}